package com.foreveross.crawl.adapter.sub.impl20140402.v3.asiana;

import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;

import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.adapter.sub.impl20140402.v3.AsianaAdapter;
import com.foreveross.crawl.common.util.DateUtil;
import com.foreveross.crawl.common.util.RegHtmlUtil;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.domain.airfreight.single.SinglePlaneInfoEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;

/**
 * 韩亚航空适配器国际实现。
 * 
 * @author luomingliang@foreveross.com
 * @version 1.0.0
 * @date 2014-07-18
 */
@SuppressWarnings({ "deprecation", "unused", "unchecked" })
public class AsianaInterAdapter extends AsianaAdapter {

	private final static Integer cabin_retry_count = 1; // 舱位获取次数
	private final static String dataUrl = "https://cn.flyasiana.com/I/ch/RevenueInternationalFareDrivenFlightSelect.do";
	private final static Map<String, String> cabinMap = ImmutableMap.of("T", "经济舱", "B", "商务舱", "F", "头等舱" /* */);

	/** ******************************** CONSTRACTORS *********************************************** */

	public AsianaInterAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}

	/** ******************************** ROUNDS *********************************************** */

	/** 抓取国际往返数据。 */
	public Object fetchRound() throws Exception {
		Map<String, String> results = Maps.newHashMap();
		String page = null;
		JSONObject flights = null;

		setCookie(); // 设置Cookie信息。
		// checkSuportAirport(); // 检查机场是否存在。
		fillAirportInfo(); // 获取机场国家信息 , 第一次填充好以后不需要再去联网获取。

		// 循环获取三种舱位的价格。
		for (String cabin : cabinMap.keySet()) {

			for (int retry = 0; retry < cabin_retry_count; retry++) {
				if(super.isUseProxyip()) Thread.sleep(30000);
				try {
					page = getPageData(cabin);
//					super.appendPageContents(page);
					validateHtml(page);
					results.put(cabin, page);
					break; // 退出重试
				} catch (Exception e) {
					if (e instanceof FlightInfoNotFoundException) throw e;
					logger.error(String.format("舱位[%s]的航班信息加载失败：%s", cabin, e.getMessage()));
					super.switchProxyipByHttClient();
				}
			}
		}

		// 解析数据并返回
		return parseRound(results);
	}

	/** ******************************** METHODS ********************************************** */

	/** 解析国际往返。 */
	private Object parseRound(Map<String, String> datas) throws Exception {
		Table<String, String, ReturnDoublePlaneInfoEntity> retables = HashBasedTable.create();
		Map<String, AbstractPlaneInfoEntity> goentitys = Maps.newHashMap();
		Map<String, AbstractPlaneInfoEntity> reentitys = Maps.newHashMap();

		// 循环解析三种舱位的航班
		for (String cabinkey : cabinMap.keySet()) {

			// 设置舱位名称
			String cabinName = cabinMap.get(cabinkey);
			String cabinData = datas.get(cabinkey);

			// 解析数据
			String jsonString="";
			try{
				jsonString = RegHtmlUtil.regStr(cabinData, "fareFamilyWithAllAvail\\s*?=\\s*?eval\\('(.*}])'\\);");
			if(StringUtils.isBlank(jsonString)){
				logger.warn(String.format("舱位[%s]没有数据", cabinName));
				continue;
			}
			}catch(Exception e){
				logger.warn(String.format("舱位[%s]没有数据", cabinName));
				continue;
			}
			JSONArray brands = JSONArray.fromObject(jsonString);
			for (int i = 0; i < brands.size(); i++) {

				// 舱位价格类型
				JSONObject brand = brands.getJSONObject(i);

				String brandName = brand.getString("brandName");
				String fareFamily = brand.getString("fareFamily");
				String currency = brand.getString("currency");
				JSONArray fareFamilyAmounts = brand.getJSONArray("fareFamilyAmounts");

				for (int j = 0; j < fareFamilyAmounts.size(); j++) {
					JSONObject fareFamilyAmount = fareFamilyAmounts.getJSONObject(j);

					String tax = getJsonString(fareFamilyAmount, "totalTax"); // 税费
					String price = getJsonString(fareFamilyAmount, "amountWithoutTax"); // 票价
					String amount = getJsonString(fareFamilyAmount, "totalAmount");// 总计
					JSONObject itineraryAvailDataOfRecommend = fareFamilyAmount.getJSONObject("itineraryAvailDataOfRecommend");
					JSONArray availDataList = itineraryAvailDataOfRecommend.getJSONArray("availDataList");
					if (availDataList.size()==0) continue;
					// 去程航班集合
					List<AbstractPlaneInfoEntity> goplaneInfos = buildPlaneInfos(availDataList.getJSONArray(0), AbstractPlaneInfoEntity.class);

					// 回程航班集合。
					List<ReturnDoublePlaneInfoEntity> replaneInfos = null;

					// 组合航班，设置回程
					for (AbstractPlaneInfoEntity absplaneInfo : goplaneInfos) {

						// 往返------------
						if (taskQueue.getIsReturn() == 1) {
							if (availDataList.size() > 1 && replaneInfos == null) {
								replaneInfos = buildPlaneInfos(availDataList.getJSONArray(1), ReturnDoublePlaneInfoEntity.class);
							}

							// 去程
							DoublePlaneInfoEntity planeInfo = (DoublePlaneInfoEntity) mergePlaneInfo(goentitys, absplaneInfo);
							// 去程舱位
							CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, brandName, null, null, null, null, null, CabinEntity.class);
							planeInfo.getCabins().add(cabin);// 添加舱位

							// 回程
							for (ReturnDoublePlaneInfoEntity replane : replaneInfos) {
								String key = planeInfo.getFlightNo() + ";" + replane.getFlightNo(); // 回程Key
								ReturnDoublePlaneInfoEntity returndouble = retables.get(planeInfo.getFlightNo(), key);
								if (returndouble == null) {
									returndouble = replane;
									retables.put(planeInfo.getFlightNo(), key, returndouble);
								}

								// 设置回程舱位。
								ReturnCabinEntity recabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, brandName, null, null, null, null, null, ReturnCabinEntity.class);
								PlaneInfoEntityBuilder.getReturnDoubleEntity(returndouble).getReturnCabins().add(recabin);// 添加仓位

								// 设置舱位关系 。
								setRelation(planeInfo, cabin, recabin, tax, price, amount);
							}

						} else {// 单程------------
							SinglePlaneInfoEntity planeInfo = (SinglePlaneInfoEntity) mergePlaneInfo(goentitys, absplaneInfo);
							// 舱位
							CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, brandName, tax, price, amount, null, null, CabinEntity.class);
							planeInfo.getCabins().add(cabin);
						}
					}
				}
			}
		}

		// 组合回程
		if (taskQueue.getIsReturn() == 1) {
			for (AbstractPlaneInfoEntity en : goentitys.values()) {
				DoublePlaneInfoEntity du = PlaneInfoEntityBuilder.getDoubleEntity(en);
				du.getReturnPlaneInfos().addAll(retables.row(du.getFlightNo()).values());
			}
		}

		List<AbstractPlaneInfoEntity> resultss = new ArrayList<AbstractPlaneInfoEntity>(goentitys.values());
		// 价格排序
		PlaneInfoEntityBuilder.buildLimitPrice(resultss);
		return resultss;
	}

	private <T extends AbstractPlaneInfoEntity> T mergePlaneInfo(Map<String, T> goentitys, AbstractPlaneInfoEntity absplaneInfo) {
		T planeInfo = goentitys.get(absplaneInfo.getFlightNo());
		if (planeInfo == null) {
			planeInfo = (T) absplaneInfo;
			goentitys.put(planeInfo.getFlightNo(), planeInfo);
		}
		return planeInfo;
	}

	/**
	 * 生成去程或回程航班的集合。
	 * 
	 * @param arrays
	 * @param isReturn
	 * @return
	 * @throws Exception
	 */
	private <T extends AbstractPlaneInfoEntity> List<T> buildPlaneInfos(JSONArray arrays, Class<T> clazz) throws Exception {
		List<T> planeInfos = Lists.newArrayList();
		T planeInfo = null;
		try {

			// 航班信息。
			for (int f = 0; f < arrays.size(); f++) {

				JSONObject flightData = arrays.getJSONObject(f);

				String bookClass = flightData.getString("bookingClass");
				String depDate = flightData.getString("departureDate");
				String arrDate = flightData.getString("arrivalDate");

				// 设置航班信息并保存中转
				planeInfo = getPlaneInfo(flightData, clazz);
				planeInfo.setStartTime(getDate(depDate));
				planeInfo.setEndTime(getDate(arrDate));

				planeInfos.add(planeInfo);
			}
		} catch (Exception e) {
			logger.error("数据解析错误 。。" + e.getMessage());
		} finally {
			planeInfo = null;
		}

		return planeInfos;
	}

	// 生成航班信息并保存中转
	private <T extends AbstractPlaneInfoEntity> T getPlaneInfo(JSONObject flightData, Class<T> clazz) throws Exception {
		T planeInfo = null;
		String flightNos = "";
		long flightDuration = 0L;

		List<TransitEntity> transits = Lists.newArrayList();
		List<ReturnTransitEntity> retransits = Lists.newArrayList();
		JSONArray segments = flightData.getJSONArray("flightInfoDatas");
		for (int m = 0; m < segments.size(); m++) {
			JSONObject segment = segments.getJSONObject(m);
			String flightNo = getJsonString(segment, "carrierCode", "flightNo");
			flightNos += !StringUtils.isBlank(flightNos) ? "|".concat(flightNo) : flightNo; // 组合航班号
			String carrierCode = getJsonString(segment, "carrierCode");
			String carrierName = getJsonString(segment, "carrierName");
			String aircraftType = getJsonString(segment, "aircraftType");// 全称：aircraftTypeDesc
			String depport = getJsonString(segment, "departureAirport");
			String depportDesc = getJsonString(segment, "departureAirportDesc");
			String arrport = getJsonString(segment, "arrivalAirport");
			String arrportDesc = getJsonString(segment, "arrivalAirportDesc");
			String arrivalDate = getJsonString(segment, "arrivalDate");
			String departureDate = getJsonString(segment, "departureDate");
			String flyingTime = getJsonString(segment, "flyingTime");

			if (!StringUtils.isBlank(flyingTime)) {
				flightDuration += convertFlightTime(flyingTime); // 设置总飞行时间
			}

			// 设置回程中转
			if (ReturnDoublePlaneInfoEntity.class.equals(clazz)) {
				ReturnTransitEntity retransit = PlaneInfoEntityBuilder.buildReturnTransitEntity(flightNo, flightNo, carrierCode, carrierName, carrierName, depport, depportDesc, arrport, arrportDesc, aircraftType);
				retransit.setStartTime(getDate(departureDate));
				retransit.setEndTime(getDate(arrivalDate));
				retransits.add(retransit);

				// 设置去程中转
			} else {
				TransitEntity transit = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, flightNo, carrierCode, carrierName, carrierName, depport, depportDesc, arrport, arrportDesc, aircraftType);
				transit.setStartTime(getDate(departureDate));
				transit.setEndTime(getDate(arrivalDate));
				transits.add(transit);
			}

			// 生成航班信息
			if (planeInfo == null) {
				if (ReturnDoublePlaneInfoEntity.class.equals(clazz)) planeInfo = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, carrierCode, carrierName, carrierName, null, null, flightNo, null, null, null, aircraftType, clazz);
				else planeInfo = (T) PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, carrierCode, carrierName, carrierName, null, null, flightNo, null, null, null, aircraftType);
			}
		}

		if (taskQueue.getIsReturn() == 1) {
			if (ReturnDoublePlaneInfoEntity.class.equals(clazz)) {
				ReturnDoublePlaneInfoEntity redouble = PlaneInfoEntityBuilder.getReturnDoubleEntity((ReturnDoublePlaneInfoEntity) planeInfo);
				if(retransits.size()>1)redouble.getReturnTransits().addAll(retransits);
				redouble.setFlightDuration(flightDuration);
			} else {
				DoublePlaneInfoEntity doub = PlaneInfoEntityBuilder.getDoubleEntity((DoublePlaneInfoEntity) planeInfo);
				if(transits.size()>1)doub.getTransits().addAll(transits);
				doub.setFlightDuration(flightDuration);
			}
		} else {
			SinglePlaneInfoEntity single = PlaneInfoEntityBuilder.getSingleEntity((SinglePlaneInfoEntity) planeInfo);
			if(transits.size()>1)single.getTransits().addAll(transits);
			single.setFlightDuration(flightDuration);// 保存总时间，每个类型都得设置一下，很坑爹
		}
		// 设置航班号为两个中转航班的组合。
		planeInfo.setFlightNo(flightNos);

		return planeInfo;
	}

	/**
	 * 设置舱位关系 。
	 */
	private void setRelation(DoublePlaneInfoEntity toFlight, CabinEntity toCabin, ReturnCabinEntity backCabin, String tax, String price, String amount) {
		CabinRelationEntity relation = new CabinRelationEntity();
		relation.setPlaneInfoEntity(toFlight);
		relation.setFullPrice(PlaneInfoEntityBuilder.getDouble(price));
		relation.setTaxesPrice(PlaneInfoEntityBuilder.getDouble(tax));
		relation.setTotalFullPrice(PlaneInfoEntityBuilder.getDouble(amount));
		relation.setCabinId(toCabin.getId());
		relation.setReturnCabinId(backCabin.getId());
		toFlight.getCabinRelations().add(relation);
	}

	/**
	 * 
	 * ******************************** FETCH DATA ***********************************************
	 * */

	/** 获取数据。 */
	private String getPageData(String cabin) throws Exception {
		logger.info(String.format("正在抓取[%s]数据...", cabinMap.get(cabin)));
		HttpRequestBase request = null;
		String page = null;
		try {

			String depArea = AIRPORTINFOS.get(taskQueue.getFromCity()); // 离开机场国家名称
			String arrArea = AIRPORTINFOS.get(taskQueue.getToCity()); // 到达机场国家名称
			String depDate = taskQueue.getFlightDate().replaceAll("-", ""); // 出发日期
			String arrDate = taskQueue.getReturnGrabDate().replaceAll("-", "");// 到达日期

			Map<String, String> params = Maps.newHashMap();
			params.put("cabinClass", cabin);
			params.put("departureAirport", taskQueue.getFromCity());
			params.put("departureArea", depArea);
			params.put("departureDate", depDate);
			params.put("arrivalAirport", taskQueue.getToCity());
			params.put("arrivalArea", arrArea);
			params.put("arrivalDate", arrDate);
			params.put("domIntType", "I");
			params.put("hidCallPage", "UPSELL_OF_INT");
			params.put("adultCount", "1");
			params.put("childCount", "0");
			params.put("infantCount", "0");
			params.put("sessionUniqueKey", "undefined");
			params.put("totalPaxCount", "1");
			params.put("tripType", "RT");

			request = getBasePost(dataUrl, params);
			request.setHeader("Host", "cn.flyasiana.com");
			request.setHeader("Referer", "http://cn.flyasiana.com/C/ch/main.do");
			enableSSL(super.getHttpClient());// 绕过SSL验证by linchuncheng
			page = excuteRequest(super.getHttpClient(), request, true);
			super.appendPageContents(page);
			super.setLenghtCount(page.length());
			rollbackProxyIp(true); // IP好用 。
		} catch (Exception e) {
			rollbackProxyIp(false);// IP不好用 。
			logger.info("获取数据异常:" + e.getMessage());
			new Exception("获取数据异常：" + e.getMessage());
		} finally {
			request = null;
		}
		return page;
	}

	/** 设置 Cookie 信息。 */
	private String setCookie() throws Exception {
		HttpRequestBase request = null;
		try {
			request = new HttpGet("http://cn.flyasiana.com/C/ch/main.do");
			request.setHeader("Host", "cn.flyasiana.com");
			request.setHeader("Referer", "http://cn.flyasiana.com/");
			String h = excuteRequest(super.getHttpClient(), request, true);
			super.appendPageContents(h);
			super.setLenghtCount(h.length());
			return h;
		} catch (Exception e) {
			logger.info(String.format("设置Cookie信息失败:%s", e.getMessage()));
			throw new Exception("设置Cookie信息失败");
		}
	}

	/** 检查到达机场是否支持。 */
	private void checkSuportAirport() throws Exception {
		String page = null;
		HttpGet get = null;
		try {
			get = new HttpGet(String.format("https://cn.flyasiana.com/I/ch/AreaAirportSearch.do?domInt=I&depAirport=%s", "PEK"));
			get.setHeader("Referer", "http://cn.flyasiana.com/C/ch/main.do");
			page = excuteRequest(super.getHttpClient(), get, true);
			super.appendPageContents(page);
			super.setLenghtCount(page.length());
			if (page.matches(taskQueue.getToCity())) {
				logger.info(String.format("没有找到[%s-%s]这条航线.", taskQueue.getFromCity(), taskQueue.getToCity()));
				throw new FlightInfoNotFoundException(String.format("没有找到[%s-%s]这条航线.", taskQueue.getFromCity(), taskQueue.getToCity()));
			}
		} catch (Exception e) {
			if (e instanceof FlightInfoNotFoundException) throw e;
			logger.info("检查到达机场出错.");
			throw new Exception("检查到达机场出错.");
		}
	}

	/** 获取机场和国家信息。 */
	private void fillAirportInfo() throws Exception {
		String page = null;
		HttpRequestBase request = null;
		try {
			// 第一次进来加载
			if (AIRPORTINFOS.values().size() == 0) {
				request = new HttpGet("http://cn.flyasiana.com/I/ch/AreaAirportSearchAjax.do?domInt=I&bizType=REV&depArrType=DEP");
				request.setHeader("Referer", "http://cn.flyasiana.com/C/ch/main.do");
				page = excuteRequest(super.getHttpClient(), request, true);
				super.appendPageContents(page);
				super.setLenghtCount(page.length());
				JSONObject json = JSONObject.fromObject(page);
				JSONArray routeCityAirportData = json.getJSONArray("RouteCityAirportData");
				for (int i = 0; i < routeCityAirportData.size(); i++) {
					JSONObject routeCity = routeCityAirportData.getJSONObject(i);
					JSONArray cityAirportDatas = routeCity.getJSONArray("cityAirportDatas");
					for (int j = 0; j < cityAirportDatas.size(); j++) {
						JSONObject cityAirport = cityAirportDatas.getJSONObject(j);
						String airport = cityAirport.getString("airport");
						String area = cityAirport.getString("area");
						AIRPORTINFOS.put(airport, area);
					}
				}
			}
		} catch (Exception e) {
			logger.info("获取城市信息失败.");
			throw new Exception("获取城市信息失败." + e.getMessage());
		} finally {
			page = null;
			request = null;
		}
	}

	/** 验证数据。 */
	private void validateHtml(String page) throws FlightInfoNotFoundException {
		if (page.matches("无可使用的航班")) {
			logger.error("没有找到相应的航班 ！");
			throw new FlightInfoNotFoundException("没有找到相应的航班 ！");
		}
	}

	/** ******************************** TOOLS *********************************************** */

	private static Map<String, String> AIRPORTINFOS = Maps.newHashMap();

	// 日期转换，把20140901020500 格字符串转换为日期。
	private static Date getDate(String dateString) {
		if (dateString == null || dateString.length() < 14) return null;
		List<String> datelist = RegHtmlUtil.regStrs(dateString, "(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})");
		datelist.remove(0);
		return DateUtil.StringToDate("yyyy-MM-dd HH:mm:ss", String.format("%s-%s-%s %s:%s:%s", datelist.toArray()));
	}

	//时间转换
	private Long convertFlightTime(String flightTime) {
		if (StringUtils.isBlank(flightTime)) {
			return 0L;
		}
		try {
			String hour = flightTime.substring(0, 2);
			String minute = flightTime.substring(2);
			long duration = Long.parseLong(hour) * 60;
			duration += Long.parseLong(minute);
			return duration * 60 * 1000;  //转换为MS
		} catch (Exception e) {
			return 0L;
		}
	}

	/** 获取JSON值。 */
	private String getJsonString(JSONObject json, String... key) {
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < key.length; i++) {
			if (json.get(key[i]) != null) result.append(json.getString(key[i]));
		}
		return result.toString();
	}

	/** 获取JSON值。 */
	private Double getJsonDouble(JSONObject json, String key, Class<?> clazz) {
		if (json.get(key) != null) return json.getDouble(key);
		return null;
	}
	
	
	 /** 
     * 访问https的网站 
     * @param httpClient 
     */  
    private static void enableSSL(HttpClient httpClient){  
        //调用ssl  
         try {  
                SSLContext sslcontext = SSLContext.getInstance("TLS");  
                sslcontext.init(null, new TrustManager[] { truseAllManager }, null);  
                SSLSocketFactory sf = new SSLSocketFactory(sslcontext);  
                sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);  
                Scheme https = new Scheme("https", sf, 443);  
                httpClient.getConnectionManager().getSchemeRegistry().register(https);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
    }  
    /** 
     * 重写验证方法，取消检测ssl 
     */  
    private static TrustManager truseAllManager = new X509TrustManager(){  
  
        public void checkClientTrusted(  
                java.security.cert.X509Certificate[] arg0, String arg1)  
                throws CertificateException {  
            // TODO Auto-generated method stub  
              
        }  
  
        public void checkServerTrusted(  
                java.security.cert.X509Certificate[] arg0, String arg1)  
                throws CertificateException {  
            // TODO Auto-generated method stub  
              
        }  
  
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {  
            // TODO Auto-generated method stub  
            return null;  
        }  
          
    }; 
}
